Skip to content

Blog

Setting Up Hector SLAM with ROS 1 for a Roboclaw-Based Rover

Setting Up Hector SLAM with ROS 1 for a Roboclaw-Based Rover

This guide details the steps to configure and use Hector SLAM with a Roboclaw-based ROS 1 robot. We’ll cover LIDAR integration, transform configuration, and how to set RViz for successful mapping and navigation.


Prerequisites

Ensure the following are set up on your system:

  1. ROS Installed: ROS Melodic or a compatible version is installed.
  2. Workspace Prepared: A catkin workspace (e.g., ~/armpi_pro) is created, built, and sourced.
  3. Hector SLAM Package:
  4. Installed and located in your workspace under src/hector_slam.
  5. Ensure hector_mapping and hector_slam_launch are available.
  6. LIDAR Sensor:
  7. Configured and publishing scan data to /scan.
  8. Roboclaw Node:
  9. Operational, publishing odometry to /odom.

Steps to Set Up Hector SLAM

1. Launch ROS Core

Open a terminal and start the ROS core:

roscore

This is required for all other nodes to communicate.


2. Launch the LIDAR and RoboClaw Nodes

To enable LIDAR data for mapping, launch the LIDAR node in a new terminal:

roslaunch ldlidar_stl_ros ld19.launch

This ensures the LIDAR sensor is active and publishing data to the /scan topic, which is necessary for Hector SLAM to create a map.


3. Configure and Launch Hector Mapping

In your workspace, navigate to the Hector SLAM launch folder:

cd ~/armpi_pro/src/hector_slam/hector_slam_launch/launch

Use the tutorial.launch file as a base for running Hector SLAM. Modify the following parameters in the launch file to match your setup:

<launch>
    <!-- Hector Mapping Node -->
    <node pkg="hector_mapping" type="hector_mapping" name="hector_mapping" output="screen">
        <param name="pub_map_odom_transform" value="true"/>
        <param name="map_frame" value="map"/>
        <param name="base_frame" value="base_link"/>
        <param name="odom_frame" value="odom"/>
        <remap from="scan" to="/scan"/>  <!-- Your LIDAR topic -->
        <remap from="odom" to="/odom"/>  <!-- Your odometry topic -->
    </node>

    <!-- Static Transform Publisher -->
    <node pkg="tf2_ros" type="static_transform_publisher" name="laser_to_base_link"
          args="0 0 0.2 0 0 0 base_link laser_link"/>

    <!-- RViz for Visualization -->
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find hector_slam_launch)/rviz_cfg/mapping.rviz" />
</launch>

Run the newly created launch file above using:

roslaunch hector_slam_launch mapping_with_odom.launch

4. Adjust RViz Configuration

To visualize the mapping process:

  1. Start RViz:

    rosrun rviz rviz
    

  2. Set the Fixed Frame to scanmatcher_frame:

  3. Navigate to Global Options in RViz.
  4. Set Fixed Frame to scanmatcher_frame.

  5. Add relevant displays:

  6. Map: Subscribe to /map for live updates.
  7. LaserScan: Subscribe to /scan to visualize LIDAR data.
  8. TF: Check the frame hierarchy and transformations.

5. Launch the Roboclaw Node

Start the Roboclaw node to handle motor control and odometry:

roslaunch roboclaw_node roboclaw.launch

This node should publish odometry data to the /odom topic, which Hector SLAM uses.


6. Run the rover

Now that the mapping and motor control nodes are active:

  • Drive the rover manually or programmatically using the following script:
        #!/usr/bin/env python2
    # -*- coding: utf-8 -*-
    """
    Example: Use Pygame to read an Xbox controller and RoboClaw on ROS Melodic (Python 2).
    No f-strings; uses .format().
    
    Make sure:
      1) roscore is running (for rospy logs).
      2) SDL_VIDEODRIVER=dummy is set if headless: export SDL_VIDEODRIVER=dummy
      3) RoboClaw is on /dev/ttyACM0 (or change param).
    """
    
    import sys
    import rospy
    import pygame
    import time
    
    # For TF in Melodic (Python 2)
    import tf
    import tf.transformations as tft
    
    # RoboClaw library (python2-compatible)
    from roboclaw import Roboclaw
    
    class XboxRoboclawDebugPy2(object):
        def __init__(self):
            # 1) ROS init
            rospy.init_node("xbox_teleop_debug_py2", anonymous=True)
            rospy.loginfo("Starting xbox_teleop_debug_py2 node...")
    
            # 2) RoboClaw Setup
            self.port_name = rospy.get_param("~port", "/dev/ttyACM0")
            self.baud_rate = rospy.get_param("~baud", 115200)
            self.address   = rospy.get_param("~address", 0x80)  # decimal 128
    
            rospy.loginfo("Attempting to open RoboClaw on {0} at {1} baud, address=0x{2:X}".format(
                self.port_name, self.baud_rate, self.address))
            self.rc = Roboclaw(self.port_name, self.baud_rate)
            if not self.rc.Open():
                rospy.logerr("Failed to open RoboClaw on {0}. Exiting.".format(self.port_name))
                sys.exit(1)
            rospy.loginfo("RoboClaw opened successfully on {0}.".format(self.port_name))
    
            # 3) Initialize Pygame
            pygame.init()
            pygame.joystick.init()
    
            joystick_count = pygame.joystick.get_count()
            rospy.loginfo("Detected {0} joystick(s).".format(joystick_count))
            if joystick_count == 0:
                rospy.logerr("No joystick detected. Exiting.")
                sys.exit(1)
    
            self.joystick = pygame.joystick.Joystick(0)
            self.joystick.init()
            rospy.loginfo("Joystick: {0}".format(self.joystick.get_name()))
    
            # 4) Speed config
            self.max_speed_cmd = 15.0  # if 127 is full speed, 20 is slower
            self.deadzone = 0.1
            rospy.loginfo("max_speed_cmd={0}, deadzone={1}".format(self.max_speed_cmd, self.deadzone))
    
            # Rate
            self.loop_rate = rospy.Rate(20)  # 20 Hz
    
            # On shutdown => stop motors
            rospy.on_shutdown(self.stop_motors)
    
            rospy.loginfo("Initialization complete. Ready to run...")
    
        def stop_motors(self):
            rospy.loginfo("Stopping motors.")
            self.rc.ForwardM1(self.address, 0)
            self.rc.ForwardM2(self.address, 0)
    
        def run(self):
            loop_counter = 0
            while not rospy.is_shutdown():
                loop_counter += 1
                # Just for debugging
                rospy.logdebug("Main loop iteration={0}".format(loop_counter))
    
                pygame.event.pump()
    
                # Example: left stick vertical = axis 1, right stick horizontal = axis 3
                forward_axis = -self.joystick.get_axis(1)
                turn_axis    =  self.joystick.get_axis(3)
    
                # Debug prints
                rospy.logdebug("Raw axes => forward_axis={0:.2f}, turn_axis={1:.2f}".format(
                    forward_axis, turn_axis))
    
                # Deadzone
                if abs(forward_axis) < self.deadzone:
                    forward_axis = 0.0
                if abs(turn_axis) < self.deadzone:
                    turn_axis = 0.0
    
                # Skid-steer mixing
                left_val  = forward_axis - turn_axis
                right_val = forward_axis + turn_axis
    
                # clamp [-1..+1]
                left_val  = max(-1.0, min(1.0, left_val))
                right_val = max(-1.0, min(1.0, right_val))
    
                # Convert to RoboClaw speed
                left_speed, left_dir   = self.convert_speed(left_val)
                right_speed, right_dir = self.convert_speed(right_val)
    
                rospy.logdebug("Mixed => L={0:.2f}, R={1:.2f} => speeds {2} ({3}), {4} ({5})".format(
                    left_val, right_val, left_speed, left_dir, right_speed, right_dir))
    
                # Send to motors
                if left_dir == "forward":
                    self.rc.ForwardM1(self.address, left_speed)
                else:
                    self.rc.BackwardM1(self.address, left_speed)
    
                if right_dir == "forward":
                    self.rc.ForwardM2(self.address, right_speed)
                else:
                    self.rc.BackwardM2(self.address, right_speed)
    
                self.loop_rate.sleep()
    
            self.stop_motors()
            pygame.quit()
    
        def convert_speed(self, val):
            """
            Convert [-1..+1] => (0..max_speed_cmd, direction).
            """
            if val >= 0:
                return (int(val * self.max_speed_cmd), "forward")
            else:
                return (int(abs(val) * self.max_speed_cmd), "backward")
    
    def main():
        try:
            node = XboxRoboclawDebugPy2()
            node.run()
        except rospy.ROSInterruptException:
            pass
        finally:
            rospy.loginfo("Exiting python2 xbox teleop script.")
    
    if __name__ == "__main__":
        main()
    
  • Watch the map being built in RViz.
  • Ensure the rover avoids obstacles and updates the map in real-time.

Key Adjustments Made

Here’s a summary of the adjustments we made to configure Hector SLAM:

  1. Set Fixed Frame in RViz to scanmatcher_frame.
  2. Modified the Hector SLAM launch file to include:
  3. pub_map_odom_transform set to true.
  4. Correct frame names (map, odom, base_link).
  5. Verified LIDAR publishing to /scan using ld19.launch.
  6. Ensured odometry data was published to /odom by the Roboclaw node.

Troubleshooting

Error: Transform Lookup Failed

If you see errors like "Lookup would require extrapolation into the future," check the following: - Ensure the system clocks are synchronized using ntp or chrony. - Verify all topics (/scan, /odom) have valid and synchronized timestamps.

LIDAR Not Publishing

Run:

rostopic echo /scan
If no data appears, ensure the LIDAR launch file is correctly configured and the sensor is operational.


Conclusion

By following these steps, you’ve successfully set up Hector SLAM on a Roboclaw-based ROS 1 rover. With LIDAR and odometry data, your rover can build a 2D map of its environment, enabling autonomous navigation and enhanced spatial awareness.

Feel free to experiment with SLAM parameters and refine your rover’s capabilities for your specific use case.

Bypassing Cloudflare with Selenium and Undetected-Chromedriver

Bypassing Cloudflare with Selenium and Undetected-Chromedriver

By combining Selenium with undetected-chromedriver (UC), you can overcome common automation challenges like Cloudflare's browser verification. This guide explores practical workflows and techniques to enhance your web automation projects.


Why Use Selenium with Undetected-Chromedriver?

Cloudflare protections are designed to block bots, posing challenges for developers. By using undetected-chromedriver with Selenium, you can:

  • Bypass Browser Fingerprinting: UC modifies ChromeDriver to avoid detection.
  • Handle Cloudflare Challenges: Seamlessly bypass "wait while your browser is verified" messages.
  • Mitigate CAPTCHA Issues: Reduce interruptions caused by automated bot checks.

Detection Challenges in Web Automation

Websites employ multiple strategies to detect and prevent automated interactions:

  • CAPTCHA Challenges: Validating user authenticity.
  • Cloudflare Browser Verification: Infinite loading screens or token-based checks.
  • Bot Detection Mechanisms: Browser fingerprinting, behavioral analytics, and cookie validation.

These barriers often require advanced techniques to maintain automation workflows.


The Solution: Selenium and Undetected-Chromedriver

The undetected-chromedriver library modifies the default ChromeDriver to emulate human-like behavior and evade detection. When integrated with Selenium, it allows:

  1. Seamless CAPTCHA Bypass: Minimize interruptions by automating responses or avoiding challenges.
  2. Cloudflare Token Handling: Automatically manage verification processes.
  3. Cookie Reuse for Session Preservation: Skip repetitive verifications by reusing authenticated cookies.

Implementation Guide: Setting Up Selenium with Undetected-Chromedriver

Step 1: Install Required Libraries

Install Selenium and undetected-chromedriver:

pip install selenium undetected-chromedriver

Step 2: Initialize the Browser Driver

Set up a Selenium session with UC:

import undetected_chromedriver as uc

# Initialize the driver
driver = uc.Chrome()

# Navigate to a website
driver.get("https://example.com")
print("Page Title:", driver.title)

# Quit the driver
driver.quit()

Step 3: Handle CAPTCHA and Cloudflare Challenges

  • Use UC to bypass passive bot checks.
  • Extract and reuse cookies to maintain session continuity:
    cookies = driver.get_cookies()
    driver.add_cookie(cookies)
    

Advanced Automation Workflow with Cookies

Step 1: Attempt Standard Automation

Use Selenium with UC to navigate and interact with the website.

Step 2: Use Cookies for Session Continuity

Manually authenticate once, extract cookies, and reuse them for automated sessions:

# Save cookies after manual login
cookies = driver.get_cookies()

# Use cookies in future sessions
for cookie in cookies:
    driver.add_cookie(cookie)
driver.refresh()

Step 3: Fall Back to Manual Assistance

Prompt users to resolve CAPTCHA or login challenges in a separate session and capture the cookies for automation.


Proposed Workflow for Automation

  1. Initial Attempt: Start with Selenium and UC for automation.
  2. Fallback to Cookies: Reuse cookies for continuity if CAPTCHA or Cloudflare challenges arise.
  3. Manual Assistance: Open a browser session for user input, capture cookies, and resume automation.

This iterative process ensures maximum efficiency and minimizes disruptions.


Conclusion

Selenium and undetected-chromedriver provide a powerful toolkit for overcoming automation barriers like CAPTCHA and Cloudflare protections. By leveraging cookies and manual fallbacks, you can create robust workflows that streamline automation processes.

Ready to enhance your web automation? Start integrating Selenium with UC today and unlock new possibilities!


References

Setting Up Browser Use with Botright

Automating web browsing while remaining undetected is critical for various use cases like scraping, testing, or automation. This guide shows you how to set up Browser Use with Botright, a robust library for stealth browsing and captcha solving.

Why Use Botright?

Botright enhances stealth browsing by leveraging techniques like fingerprint masking and AI-driven captcha solving. It integrates seamlessly with Playwright, making it perfect for stealthy automation tasks.

Prerequisites

Ensure the following tools and dependencies are installed:

  1. Python 3.11 or Higher: Install from python.org.
  2. pip: Included with Python.
  3. Browser Use: The main framework.
  4. Botright: For stealth features and advanced automation.

Install Required Packages

Run the following commands to set up your environment:

# Clone the repository
git clone https://github.com/browser-use/browser-use
cd browser-use

# Install uv for environment management
pip install uv

# Create a virtual environment
uv venv --python 3.11

# Activate the virtual environment
source .venv/bin/activate # For Linux/macOS
.venv\Scripts\activate    # For Windows

# Install Browser Use with development dependencies
uv pip install . ."[dev]"

# Install Botright
pip install botright
playwright install

Implementation

Follow these steps to integrate Botright with Browser Use:

1. Update main.py

Replace the Playwright browser initialization with Botright’s initialization. Here’s the updated main.py:

import asyncio
import botright

async def main():
    # Initialize Botright with enhanced stealth options
    botright_client = await botright.Botright(headless=False)

    # Launch a browser instance
    browser = await botright_client.new_browser()

    # Open a new page
    page = await browser.new_page()

    # Navigate to a website
    await page.goto("https://example.com")

    # Perform browser actions (e.g., take a screenshot)
    await page.screenshot(path="screenshot.png")
    print("Screenshot saved!")

    # Close the browser and Botright client
    await browser.close()
    await botright_client.close()

if __name__ == "__main__":
    asyncio.run(main())

2. Configure Environment Variables

Set up required API keys by copying the example environment file:

cp .env.example .env

Add the following to your .env file:

BOTRIGHT_API_KEY=your_botright_api_key

3. Run the Script

Run your updated main.py script:

python main.py

4. What Happens?

  1. Browser Initialization: Botright launches a Chromium-based browser instance with stealth enhancements.
  2. Website Navigation: The browser navigates to the specified URL (https://example.com).
  3. Screenshot Capture: A screenshot is taken and saved as screenshot.png.

Debugging Tips

  1. Botright Logs: Use print statements or Botright's built-in logging for debugging.
  2. Browser Errors: Verify the installed version of Playwright and Botright.
  3. Environment Issues: Ensure your .env file contains the correct API keys.

Advanced Features

Captcha Solving

To solve captchas, use Botright's built-in methods. Example:

await page.solve_captcha()

Fingerprint Masking

Botright automatically handles fingerprint masking. To customize this, refer to the Botright documentation.

Conclusion

By integrating Botright with Browser Use, you unlock advanced stealth browsing capabilities ideal for sensitive automation tasks. This setup provides a solid foundation for further enhancements, such as integrating AI-based analysis or multi-page navigation workflows.

AWS Lambda and Blender: Revolutionizing 3D Rendering in the Cloud

One idea that has been on my ideological backburner for several years now is the concept of using AWS Lambda for rendering a three-dimensional STL or other Blender-compatible file for GitHub contributions. Since the inception of this idea, I've significantly refined my understanding of 3D printing and Python scripting, which has allowed me to develop a more robust and scalable solution.

The Concept

The core concept revolves around leveraging AWS Lambda for rendering 3D scenes—a solution tailored for projects requiring high scalability and rapid turnaround times. This technique excels in scenarios involving numerous simpler assets that must be rendered swiftly, effectively harnessing the computational prowess of cloud technology.

The Implementation

The integration of Blender, a popular open-source 3D graphics software, running on AWS Lambda, epitomizes this blend of flexibility and computational efficiency. This approach is ideal for assets that fit within Lambda's constraints, currently supporting up to 6 vCPUs and 10GB of memory. For more demanding rendering needs, options like EC2 instances or AWS Thinkbox Deadline provide enhanced computational capacity, making them suitable for complex tasks.

The Workflow

The workflow for this implementation is straightforward:

  1. Upload the Blender file to an S3 bucket: Begin by uploading the Blender file to an S3 bucket, ensuring it is accessible to the Lambda function.
  2. Invoke the Lambda function: Trigger the Lambda function to render the 3D scene using Blender.
  3. Retrieve the rendered image: Once the rendering is complete, retrieve the rendered image from the S3 bucket.

The Benefits

The benefits of this approach are manifold:

  • Scalability: AWS Lambda's scalability ensures that rendering tasks can be efficiently distributed across multiple instances, enhancing performance.
  • Cost-Effectiveness: Pay only for the compute time consumed, making it a cost-effective solution for rendering tasks.
  • Flexibility: The ability to scale up or down based on project requirements offers unparalleled flexibility.
  • Efficiency: The seamless integration of Blender with AWS Lambda streamlines the rendering process, enhancing efficiency.

Credits

The inspiration for this approach was drawn from a detailed implementation by Theodo in 2021, showcasing how Blender can be effectively adapted for serverless architecture. This concept offers transformative potential in the 3D rendering landscape, demonstrating how cloud technologies can redefine efficiency and scalability in creative workflows.

Conclusion

The fusion of AWS Lambda and Blender represents a paradigm shift in 3D rendering, offering a potent solution for projects requiring rapid, scalable rendering capabilities. By leveraging the computational prowess of AWS Lambda and the versatility of Blender, developers can unlock new possibilities in the 3D rendering domain, revolutionizing creative workflows and enhancing efficiency.

Fine-Tuning GPT-4o-mini: A Comprehensive Guide

Fine-tuning GPT-4o-mini allows you to create a customized AI model tailored to specific needs, such as generating content or answering domain-specific questions. This guide will walk you through preparing your data and executing the fine-tuning process.


Step 1: Prepare Your Dataset

Dataset Format

Fine-tuning requires a .jsonl dataset where each line is a structured chat interaction. For example:

{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the capital of France?"}, {"role": "assistant", "content": "The capital of France is Paris."}]}
{"messages": [{"role": "system", "content": "You are a travel expert."}, {"role": "user", "content": "What are the best places to visit in Europe?"}, {"role": "assistant", "content": "Some of the best places to visit in Europe include Paris, Rome, Barcelona, and Amsterdam."}]}

Automate Dataset Preparation

Use the Text to JSONL Converter available at Streamlit to convert .txt files into .jsonl format. Ensure you have at least 10 samples.


Step 2: Fine-Tune GPT-4o-mini

Required Code for Fine-Tuning

Save your stories.jsonl file and run the following Python script to initiate fine-tuning:

from openai import OpenAI
import openai
import os

# Initialize OpenAI client and set API key
openai.api_key = os.getenv("OPENAI_API_KEY")
client = OpenAI()

# Step 1: Upload the training file
response = client.files.create(
    file=open("stories.jsonl", "rb"),  # Replace with the correct path to your JSONL file
    purpose="fine-tune"
)

# Extract the file ID from the response object
training_file_id = response.id
print(f"File uploaded successfully. File ID: {training_file_id}")

# Step 2: Create a fine-tuning job
fine_tune_response = client.fine_tuning.jobs.create(
    training_file=training_file_id,
    model="gpt-4o-mini-2024-07-18"  # Replace with the desired base model
)

# Output the fine-tuning job details
print("Fine-tuning job created successfully:")
print(fine_tune_response)

Explanation of the Code

  1. Initialize OpenAI Client:
  2. The script imports the openai library and initializes the API using your key stored in the OPENAI_API_KEY environment variable.

  3. Upload Training File:

  4. The script uploads your stories.jsonl file to OpenAI's servers for processing.

  5. Create Fine-Tuning Job:

  6. The uploaded file is referenced to create a fine-tuning job for the gpt-4o-mini-2024-07-18 model. Replace this with the desired base model as needed.

  7. Monitor Job Details:

  8. The script outputs the details of the fine-tuning job, including its status and other metadata.

Best Practices for Fine-Tuning

  1. Quality Dataset: Ensure the dataset is diverse and adheres to the required structure.
  2. System Role Definition: Use clear instructions in the system role to guide the model’s behavior.
  3. Testing and Iteration: Evaluate the fine-tuned model and refine the dataset if necessary.

By using this step-by-step guide and the provided Python script, you can fine-tune the GPT-4o-mini model for your unique use case effectively. Happy fine-tuning!

Setting Up Venom for WhatsApp Translation

Automating WhatsApp messaging can be a powerful tool for customer service, personal projects, or language translation. Using Venom and Google Translate, this guide will show you how to build a script that translates incoming Spanish messages to English and replies in Spanish.

Why Use Venom?

Venom is a robust Node.js library that allows you to interact with WhatsApp Web. It’s perfect for creating bots, automating tasks, or building translation systems like the one we’ll create here.

Prerequisites

Before diving in, ensure you have the following installed:

  1. Node.js: Install from Node.js Official Website.
  2. npm or yarn: Installed alongside Node.js.
  3. Google Translate Library: For text translation.
  4. Venom: For WhatsApp automation.

Install Required Packages

Run the following commands to install the required libraries:

npm install venom-bot translate-google crypto

Implementation

Here’s how to set up and use Venom to translate WhatsApp messages:

1. Initialize the Project

Create a new file named whatsapp_translator.js and start with the following boilerplate:

const venom = require('venom-bot');
const translate = require('translate-google');
const crypto = require('crypto');

2. Set Up Your WhatsApp Contacts

Define your own WhatsApp ID (for self-messages) and the target contact:

const MY_CONTACT_ID = '12345678900@c.us'; // Your number
const TARGET_CONTACT_ID = '01234567890@c.us'; // Target contact's number

3. Implement the Translation Logic

Here’s the full script for translating messages and avoiding duplicates using a hash set:

// Hash sets to prevent duplicate message processing
const processedMessageHashes = new Set();

venom
  .create({
    session: 'my-whatsapp-session',
    multidevice: true,
  })
  .then((client) => start(client))
  .catch((err) => console.error('Error starting Venom:', err));

function start(client) {
  console.log(`Listening for messages between yourself (${MY_CONTACT_ID}) and ${TARGET_CONTACT_ID}.`);

  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

  // Function to generate a hash for deduplication
  function generateHash(messageBody) {
    return crypto.createHash('sha256').update(messageBody).digest('hex');
  }

  // Periodically check for new messages in the self-chat
  setInterval(async () => {
    try {
      const messages = await client.getAllMessagesInChat(MY_CONTACT_ID, true, true);
      for (const message of messages) {
        processMessage(client, message, generateHash);
      }
    } catch (err) {
      console.error('Error retrieving self-chat messages:', err);
    }
  }, 2000); // Check every 2 seconds

  // Handle incoming messages
  client.onMessage((message) => processMessage(client, message, generateHash));
}

async function processMessage(client, message, generateHash) {
  const messageHash = generateHash(message.body);

  // Skip if the message has already been processed
  if (processedMessageHashes.has(messageHash)) {
    return;
  }

  // Mark the message as processed
  processedMessageHashes.add(messageHash);

  try {
    if (message.from === MY_CONTACT_ID && message.to === MY_CONTACT_ID) {
      console.log('Message is from you (self-chat).');

      // Translate English to Spanish and send to the target contact
      const translatedToSpanish = await translate(message.body, { to: 'es' });
      console.log(`Translated (English → Spanish): ${translatedToSpanish}`);

      await client.sendText(TARGET_CONTACT_ID, translatedToSpanish);
      console.log(`Sent translated message to ${TARGET_CONTACT_ID}: ${translatedToSpanish}`);
    } else if (message.from === TARGET_CONTACT_ID && !message.isGroupMsg) {
      console.log('Message is from the target contact.');

      // Translate Spanish to English and send to the self-chat
      const translatedToEnglish = await translate(message.body, { to: 'en' });
      console.log(`Translated (Spanish → English): ${translatedToEnglish}`);

      const response = `*Translation (Spanish → English):*\nOriginal: ${message.body}\nTranslated: ${translatedToEnglish}`;
      await client.sendText(MY_CONTACT_ID, response);
      console.log(`Posted translation to yourself: ${MY_CONTACT_ID}`);
    }
  } catch (error) {
    console.error('Error processing message:', error);
    // Remove the hash if processing fails
    processedMessageHashes.delete(messageHash);
  }
}

4. Run the Script

Execute the script using Node.js:

node whatsapp_translator.js

5. What Happens?

  1. Messages you send to yourself (in English) are translated to Spanish and sent to the target contact.
  2. Messages from the target contact (in Spanish) are translated to English and sent to your self-chat.

Debugging Tips

  1. Verify Contact IDs: Ensure MY_CONTACT_ID and TARGET_CONTACT_ID are correctly defined.
  2. Check Logs: Use console.log statements to debug the flow of messages.
  3. Dependency Issues: Reinstall packages with npm install if you encounter errors.

Conclusion

This script automates translation for WhatsApp messages, enabling seamless communication across languages. By leveraging Venom and Google Translate, you can extend this setup to support additional languages, integrate with databases, or even build advanced customer service tools. With this foundation, the possibilities are endless!

Building an Agentic Web Scraping Pipeline for Crypto and Meme Coins

How to Build an Agentic Web Scraping Pipeline for Crypto and Meme Coins

Agentic web scraping revolutionizes data collection by leveraging advanced scraping tools and LLM-based reasoning to analyze websites for actionable insights. This guide demonstrates how to build a closed-loop pipeline for analyzing popular crypto and meme coin websites to enhance trading strategies.


Websites to Scrape

The following websites will serve as data inputs for the pipeline:

  1. Movement Market
    Facilitates buying and selling meme coins with email and credit card integration.

  2. Raydium
    A decentralized exchange for trading tokens and coins.

  3. Jupiter
    A platform for seamless token swaps.

  4. Rugcheck
    A tool for evaluating meme coins and identifying scams.

  5. Photon Sol
    A browser-based solution for trading low-cap coins.

  6. Cielo Finance
    Offers a copy-trading platform to follow top-performing wallets.


Step 1: Structuring Data for Public Websites

For effective analysis, raw HTML data from these websites must be structured into human-readable Markdown.

Tool: Firecrawl

Use Firecrawl to scrape and format the websites:

Example: Scraping Movement Market

import requests

FIRECRAWL_API = "https://api.firecrawl.com/v1/scrape"
API_KEY = "your_firecrawl_api_key"

def scrape_with_firecrawl(url):
    headers = {"Authorization": f"Bearer {API_KEY}"}
    data = {"url": url, "output": "markdown"}
    response = requests.post(FIRECRAWL_API, json=data, headers=headers)

    if response.status_code == 200:
        return response.json().get("markdown")
    else:
        print(f"Error: {response.status_code} - {response.text}")
        return None

markdown_data = scrape_with_firecrawl("https://movement.market/")
print(markdown_data)

Repeat the process for all listed websites to create structured Markdown files.


Step 2: Analyze Public Data with Reasoning Agents

Once the data is structured, LLMs can be used to analyze trends, extract features, and provide actionable insights.

Example: Analyzing Data with OpenAI API
import openai

openai.api_key = "your_openai_api_key"

def analyze_markdown(markdown_data):
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=f"Analyze this Markdown data to identify trading opportunities and community sentiment:\n\n{markdown_data}",
        max_tokens=1000
    )
    return response.choices[0].text.strip()

markdown_example = "# Example Markdown\nThis is an example of markdown content for analysis."
analysis = analyze_markdown(markdown_example)
print(analysis)

Step 3: Scraping Private Data with Web Automation

For websites requiring interaction (e.g., logins or dynamic content), use Python's Playwright library with AgentQL for advanced navigation and data extraction.

Example: Scraping Photon Sol with Playwright and AgentQL

Install Playwright and AgentQL:

pip install playwright
playwright install

Write the Python Script:

from playwright.sync_api import sync_playwright

def scrape_photon_sol():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()

        # Navigate to Photon Sol
        page.goto("https://photon-sol.tinyastro.io/")

        # Simulate interactions if needed
        page.wait_for_timeout(3000)  # Wait for the page to load completely
        content = page.content()

        print(content)  # Print or save the page content
        browser.close()

scrape_photon_sol()

This approach ensures data can be extracted even from dynamic websites.


Step 4: Automating the Pipeline

Use Python-based automation tools like Apache Airflow to schedule and run the scraping and analysis pipeline.

Example: Airflow Configuration for the Pipeline
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
from datetime import datetime

def scrape():
    # Add scraping logic for all websites here
    print("Scraping data...")

def analyze():
    # Add analysis logic here
    print("Analyzing data...")

with DAG('crypto_pipeline', start_date=datetime(2024, 11, 25), schedule_interval='@daily') as dag:
    scrape_task = PythonOperator(task_id='scrape', python_callable=scrape)
    analyze_task = PythonOperator(task_id='analyze', python_callable=analyze)

    scrape_task >> analyze_task

Insights from Websites

Here's what you can focus on while analyzing the scraped data:

  1. Movement Market: Review ease of use, transaction speed, and user feedback.
  2. Raydium: Analyze liquidity and trading fees for tokens.
  3. Jupiter: Evaluate swap rates and platform efficiency.
  4. Rugcheck: Identify red flags in meme coin projects to avoid scams.
  5. Photon Sol: Assess platform usability for low-cap token trading.
  6. Cielo Finance: Analyze wallet strategies and portfolio performance.

Step 5: Closing the Loop

To maintain a closed-loop pipeline, configure the workflow to automatically re-scrape websites at regular intervals and update analyses with new data. This ensures decisions are based on the latest information.


Conclusion

By integrating structured scraping, advanced analysis, and automation, this agentic pipeline enables real-time insights into the crypto and meme coin ecosystem. Use the steps outlined above to stay ahead in the volatile world of meme coins while minimizing risks and maximizing returns. 🚀

Installing ROS 1 on Raspberry Pi

Installing ROS 1 on Raspberry Pi

Robot Operating System (ROS) is an open-source framework widely used for robotic applications. This guide walks you through installing ROS 1 (Noetic) on a Raspberry Pi running Ubuntu. ROS 1 Noetic is the recommended version for Raspberry Pi and supports Ubuntu 20.04.


Prerequisites

Before starting, ensure you have the following:

  • Raspberry Pi 4 or later with at least 4GB of RAM (8GB is recommended for larger projects).
  • Ubuntu 20.04 installed on the Raspberry Pi (Desktop or Server version).
  • Internet connection for downloading and installing packages.

Step 1: Set Up Your Raspberry Pi

  1. Update and Upgrade System Packages:
    sudo apt update && sudo apt upgrade -y
    
  2. Install Required Dependencies:
    sudo apt install -y curl gnupg2 lsb-release
    

Step 2: Configure ROS Repositories

  1. Add the ROS Repository Key:

    sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
    

  2. Add the ROS Noetic Repository:

    echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/ros-latest.list
    

  3. Update Package List:

    sudo apt update
    


Step 3: Install ROS 1 Noetic

  1. Install the Full ROS Desktop Version:

    sudo apt install -y ros-noetic-desktop-full
    

  2. Verify the Installation: Check the installed ROS version:

    rosversion -d
    
    This should return noetic.


Step 4: Initialize ROS Environment

  1. Set Up ROS Environment Variables:

    echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
    source ~/.bashrc
    

  2. Install rosdep: rosdep is a dependency management tool for ROS:

    sudo apt install -y python3-rosdep
    

  3. Initialize rosdep:

    sudo rosdep init
    rosdep update
    


Step 5: Test the ROS Installation

  1. Run roscore: Start the ROS master process:

    roscore
    
    Leave this terminal open.

  2. Open a New Terminal and Run turtlesim: Launch a simple simulation:

    rosrun turtlesim turtlesim_node
    

  3. Move the Turtle: Open another terminal and control the turtle using:

    rosrun turtlesim turtle_teleop_key
    
    Use the arrow keys to move the turtle in the simulation.


Step 6: Install Additional ROS Tools

To enhance your ROS setup, install the following:

  1. catkin Tools:

    sudo apt install -y python3-catkin-tools
    

  2. Common ROS Packages:

    sudo apt install -y ros-noetic-rviz ros-noetic-rqt ros-noetic-rqt-common-plugins
    

  3. GPIO and Hardware Libraries (for Pi-specific projects):

    sudo apt install -y wiringpi pigpio
    


Troubleshooting

  • Issue: rosdep not initializing properly.
    Fix: Ensure network connectivity and retry:

    sudo rosdep init
    rosdep update
    

  • Issue: ROS environment variables not set.
    Fix: Manually source the ROS setup file:

    source /opt/ros/noetic/setup.bash
    


Conclusion

Your Raspberry Pi is now configured with ROS 1 Noetic, ready for robotic projects. With this setup, you can develop and deploy various ROS packages, integrate hardware, and experiment with advanced robotic systems.

Happy building!

Harminder Singh Nijjar's Digital Art Catalog

2024-11-25: While sitting on the dining table drinking a Celsius Peach Vibe, I decided to create a quick digital drawing of the can next to a container of JIF peanut butter. The drawing was done on my MobiScribe WAVE using the stylus that came with the device. The MobiScribe WAVE is a great tool for digital art, and I enjoy using it for quick sketches and drawings. JIF + Celsius Peach Vibe

2024-11-26 20:17: Today I drew a quick sketch of two wolf pups howling at the moon. Full Moon Pups

Agentic Web Scraping in 2024

Web scraping best practices have evolved significantly in the past couple of years, with the rise of agentic web scraping marking a new era in data collection and analysis. In this post, we'll explore the concept of agentic web scraping, its benefits, and how it is transforming the landscape of data-driven decision-making.

Evolution of Web Scraping

Typically, web scraping involved extracting data from websites by mimiking browser behaviour through HTTP requests and web automation frameworks like Selenium, Puppeteer, or Playwright. This process required developers to write specific code for each website, making it time-consuming, error-prone, and susceptible to changes in website structure. So much so that 50% to 70% of engineering resources in data aggregation teams were spent on scraping stystems early on. However, with the advent of agentic web scraping, this approach has been revolutionized. LLMs are able to make sense of any data thrown at them, allowing them to understand large amounts of raw HTML and make decisions based on it.

This comes with a drawback, however. The more unstructured data you throw at an LLM, the more likely it is to make mistakes and the more tokens are consumed. This is why it's important to have as close to structured, human-readable data as possible.

Structuring Data for Agentic Web Scraping

In order to be able to use LLM Scraper Agents and Reasoning Agents, we need to convert raw HTML data into a more structured format. Markdown is a great choice for this, as it is human-readable and easily parsed by LLMs. After converting scraped data into structured markdown, we can feed it into LLM Scraper Agents and Reasoning Agents to make sense of it and extract insights.

Web Scraper Agents for Public Data

Public data is data that is freely available on the web, such as news articles, blog posts, and product descriptions. This data can be scraped and used for various purposes and does not require any special permissions such as bypassing CAPTCHAs or logging in.

Some APIs that can be used to convert raw HTML data into structured markdown include:

Firecrawl

Firecrawl turns entire websites into clean, LLM-ready markdown or structured data. Scrape, crawl and extract the web with a single API

Output: Good quality markdown with most hyperlinks preserved

Rate limit: 1000 requests per minute

Cost: $0.06 per 100 pages

Jina

Turn a website into a structured data by adding r.jina.ai in front of the URL.

Output: Focuses primarily on extracting content rather than preserving hyperlinks

Rate limit: 1000 requests per minute

Cost: Free

Spider Cloud

Spider is a leading web crawling tool designed for speed and cost-effectiveness, supporting various data formats including LLM-ready markdown.

Output: Happy medium between Firecrawl and Jina with good quality markdown

Rate limit: 50000 requests per minute

Cost: $0.03 per 100 pages

Web Scraper Agents for Private Data

As mentioned earlier, web automation frameworks like Selenium, Puppeteer, or Playwright are used to scrape private data that requires interaction to access restricted areas of a website. These tools can now be used to build agentic web scraping systems that can understand and reason about the data they collect. However, the issue with these tools is determining which UI elements to interact with to access the abovementioned restricted areas of a site. This is where AgentQL comes in.

AgentQL

AgentQL allows web automation frameworks to accurately navigate websites, even when the website structure changes.

Rate limit: 10 API calls per minute

Cost: $0.02 per API call

Using AgentQL in conjunction with web automation frameworks enables developers to build agentic web scraping systems that can access and reason about private data, making the process more efficient and reliable.

How AgentQL Works

Some examples of actions we're able to perform with AgentQL along with Playwright or Selenium include:

  • Save and load authenticated state
  • Wait for a page to load
  • Close a cookie dialog
  • Close popup windows
  • Compare product prices across multiple websites

Conclusion

Agentic web scraping is transforming the way data is collected and analyzed, enabling developers to build systems that can understand and reason about the data they collect. By structuring data in a human-readable format like markdown and using tools like LLM Scraper Agents, Reasoning Agents, and AgentQL, developers can create efficient and reliable web scraping systems that can access both public and private data. This new approach to web scraping is revolutionizing the field of data-driven decision-making and opening up new possibilities for data analysis and insights.